/**
 * Debug-JS Profiling support plugin.
 *
 * @author Matias Mirabelli <admin@moyrax.com>
 */
Debugger.Profiler = Debugger.Plugins.register(Debugger.PluginType.PROFILING,
                                              null, function() {
  /**
   * @class Profile descriptor. This class keeps track of all operations in
   * a single method. A new <code>Profile</code> instance will be created
   * for each method.
   *
   * Note: In the current version there's no concurrency control, so Profiler
   * may have inconsistency in cases on the same Wrapper is used with
   * different objects and Ajax requests are made by these objects
   * using as callback a method registered in the Profiler previously.
   *
   * @param theMethodName {String} Name of the method.
   *
   * @author Matías Mirabelli <admin@moyrax.com>
   * @since 0.11
   */
  var Profile = function(theMethodName) {
    var lastTimeRef = null;
    var execCount = 0;
    var totalTime = 0;
    var minTime = 1000000;
    var maxTime = 0;
    var entries = [];
    var lastEntry = null;

    var publicInterface = {
      /**
       * Method name.
       */
      methodName : theMethodName,

      /**
       * Method execution starts.
       */
      start : function () {
        lastTimeRef = new Date().getTime();
        lastEntry = new Profile.Entry(this.methodName, lastTimeRef);
        execCount++;
      },

      /**
       * Method execution ends.
       */
      finalize : function() {
        lastEntry.endTime = new Date().getTime();
        lastEntry.totalTime = lastEntry.endTime - lastEntry.startTime;
        totalTime += (lastEntry.endTime - lastEntry.startTime);

        if (lastEntry.totalTime > maxTime) {
          maxTime = lastEntry.totalTime;
        }

        if (lastEntry.totalTime < minTime) {
          minTime = lastEntry.totalTime;
        }

        entries.push(lastEntry);
      },

      /**
       * Returns the total time, in milliseconds, that the method related to
       * this profile has been executing.
       */
      getTotalTime : function() {
        return totalTime;
      },

      /**
       * Returns how many times this method has been called.
       */
      getExecutionCount : function() {
        return execCount;
      }
    };
    return publicInterface;
  };

  /**
   * @class Entry related to a profile. Each entry represents
   * a call to a method.
   *
   * @param theMethodName {String} Name of the method.
   *
   * @author Matías Mirabelli <admin@moyrax.com>
   * @since 0.11
   */
  Profile.Entry = function(theMethodName, startTime) {
    var publicInterface = {
      methodName : theMethodName,

      startTime : startTime || (new Date().getTime()),

      endTime : 0,

      totalTime : 0
    };
    return publicInterface;
  };

  /**
   * Profile list.
   *
   * @type Array[Profile]
   */
  var _profiles = [];

  /* Visible interface. */
  var publicInterface = {
    /**
     * Pushs a method into the stack.
     *
     * @param theData {Object} Debugging data for this event.
     */
    push : function(theData) {},

    /**
     * Removes the last method from the stack.
     *
     * @param theData {Object} Debugging data for this event.
     */
    pop : function(theData) {},

    /**
     * Called before the wrapper executes the original method.
     *
     * @param theData {Object} Data passed by the wrapper.
     */
    prolog : function (theData) {
      if (!_profiles[theData.name]) {
        _profiles[theData.name] = new Profile(theData.name);
      }

      _profiles[theData.name].start();
    },

    /**
     * Called after the wrapper executes the original method.
     *
     * @param theData {Object} Data passed by the wrapper.
     */
    epilog : function (theData) {
      if (!_profiles[theData.name]) {
        Debugger.raiseError("Profiler couldn't find the method descriptor.");
      }

      _profiles[theData.name].finalize();
    },

    /**
     * Returns a Profile related to the specified method name.
     *
     * @param aMethodName {String} Method name.
     */
    getProfile : function(aMethodName) {
      return _profiles[aMethodName] || null;
    }
  };

  return publicInterface;
}());
